#include "samplers.fx"
#include "shading.fx"

struct VS_IN
{
	float3	position	:	POSITION;
	float3	normal		:	NORMAL;
	float3	tangent		:	TANGENT;
	float2	tex			:	TEX_COORDS;
};
//____________
struct ControlPoint
{
	float3	position	:	POSITION;
};
//____________
struct quadTessFactors
{
	float	edge[4]		:	SV_TessFactor;
	float	inside[2]	:	SV_InsideTessFactor;
};
//____________
struct DS_OUT
{
	float4	position	:	SV_POSITION;
	float3  wNormal		:	NORMAL;
};

//________ V A R I A B L E S____________________________________________________________________________

Texture2D	texTerrainHeight;
float4x4	m_World;
float4x4	m_WorldViewProj;
float		fInsideTess;
float		fEdgeTess;
float3		vPosition;
float3		vTargetPos;

float3 arrControl_Points[16]  = 

{	

	// worm-like
	float3(-0,3,1),	 float3(-1,3,0),	float3(1,3,0),	float3(0,3,1),
	float3(-0,1,1),	 float3(-1,1,0),	float3(1,1,0),	float3(0,1,1),
	float3(-0,-1,1), float3(-1,-1,0),	float3(1,-1,0),	float3(0,-1,1),
	float3(-0,-3,1), float3(-1,-3,-0),	float3(1,-3,0),	float3(0,-3,1)

};


RasterizerState rs_Wireframe
{
	//CullMode = Back;
	CullMode = None;
	FillMode = Wireframe;
};


//________ F U N C T I O N S________________________________________________________________________________

// Bernstein basis function for n=3 (cubic) - index e [0,3]
float4 Bernstein(float t)
{
	float4 res;
	float tmp = 1.0f - t;

	res.x = tmp * tmp * tmp;	// (1-t)^3
	res.y = tmp * tmp * 3.0f*t;	// 3t*(1-t)^2
	res.z = tmp * 3.0f*t*t;		// (1-t)*3t^2
	res.w = t*t*t;	     		// t^3

	return res;
}

float4 dBernstein(float t)
{
	float4 res;
	float tmp = 1 - t;
	
	res.x = -3*tmp*tmp;	     		// -3(1-t)^2
	res.y = 3*tmp*tmp - 6*t*tmp;	// 3(1-t)^2 - 6t(1-t)
	res.z = 6*t*tmp - 3*t*t;		// 6t(1-t)-3t^2
	res.w = 3*t*t;					// 3t^2

	return res;
}

float getTerrainHeight(float3 wpos)
{
	float GRID_WIDTH = 200.0f;

	float u = wpos.x / GRID_WIDTH + 0.5f;
	float v = wpos.y / GRID_WIDTH + 0.5f;

	return texTerrainHeight.SampleLevel(s_linear, float2(u,v), 0).x;

}



float3 evaluateBezierSurface(const OutputPatch<ControlPoint, 16> patch, float4 basisU, float4 basisV)
{
	float3 res;

	res  = basisV.x * (basisU.x*patch[0].position +  basisU.y*patch[1].position +  basisU.z*patch[2].position +  basisU.w*patch[3].position);
	res += basisV.y * (basisU.x*patch[4].position +  basisU.y*patch[5].position +  basisU.z*patch[6].position +  basisU.w*patch[7].position);
	res += basisV.z * (basisU.x*patch[8].position +  basisU.y*patch[9].position +  basisU.z*patch[10].position + basisU.w*patch[11].position);
	res += basisV.w * (basisU.x*patch[12].position + basisU.y*patch[13].position + basisU.z*patch[14].position + basisU.w*patch[15].position);
	
	return res;
}


//________ S H A D E R S________________________________________________________________________________


ControlPoint VS_BezSurface(VS_IN vin)
{
	ControlPoint vout;

	vout.position = vin.position;
	return vout;
}
//_______________
											// ctrl points per patch
quadTessFactors ConstantHS_BezSurface(InputPatch<ControlPoint, 1> patch, uint patchID : SV_PrimitiveID) // Constant Hull Shader
{
	quadTessFactors tout;

	tout.edge[0] = fEdgeTess;
	tout.edge[1] = fEdgeTess;
	tout.edge[2] = fEdgeTess;
	tout.edge[3] = fEdgeTess;
	
	tout.inside[0] = fInsideTess;
	tout.inside[1] = fInsideTess;

	return tout;
}
//_______________

[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_ccw")]
[outputcontrolpoints(16)]
[patchconstantfunc("ConstantHS_BezSurface")]
[maxtessfactor(64.0f)]
ControlPoint ControlPointHS_BezSurface_Integer(InputPatch<ControlPoint, 1> patch,	   uint ctrlPoint_index : SV_OutputControlPointID, 
																		       uint patchId	     	: SV_PrimitiveID)
{
	float3 X = float3(0,0,1);
	float3 Y = float3(1,0,0);
	float3 Z = float3(0,1,0);

	// Set orientation towards target pos 
	Z = float3(0,1,0);
	Y = normalize(vTargetPos - vPosition);
	X = cross(Z,Y);


	float3x3 xform = { X, Y, Z };
	

	// Output 4x4 control points
	ControlPoint p;
	p.position = arrControl_Points[ctrlPoint_index];
	
	p.position = mul(p.position, xform) + vPosition;

	return p;
}
//_______________


[domain("quad")]
[partitioning("fractional_even")]
[outputtopology("triangle_ccw")]
[outputcontrolpoints(16)]
[patchconstantfunc("ConstantHS_BezSurface")]
[maxtessfactor(64.0f)]
ControlPoint ControlPointHS_BezSurface_FracEven(InputPatch<ControlPoint, 1> patch,	   uint ctrlPoint_index : SV_OutputControlPointID, 
																		       uint patchId	     	: SV_PrimitiveID)
{
	float3 X = float3(0,0,1);
	float3 Y = float3(1,0,0);
	float3 Z = float3(0,1,0);

	// Set orientation towards target pos
	Z = float3(0,1,0);
	Y = normalize(vTargetPos - vPosition);
	X = cross(Z,Y);

	float3x3 xform = { X, Y, Z };

	// Output 4x4 control points
	ControlPoint p;
	p.position = arrControl_Points[ctrlPoint_index];
	p.position = mul(p.position, xform) + vPosition;
	return p;
}
//_______________

[domain("quad")]
[partitioning("fractional_odd")]
[outputtopology("triangle_ccw")]
[outputcontrolpoints(16)]
[patchconstantfunc("ConstantHS_BezSurface")]
[maxtessfactor(64.0f)]
ControlPoint ControlPointHS_BezSurface_FracOdd(InputPatch<ControlPoint, 1> patch,	   uint ctrlPoint_index : SV_OutputControlPointID, 
																		       uint patchId	     	: SV_PrimitiveID)
{
	float3 X = float3(0,0,1);
	float3 Y = float3(1,0,0);
	float3 Z = float3(0,1,0);

	// Set orientation towards target pos
	Z = float3(0,1,0);
	Y = normalize(vTargetPos - vPosition);
	X = cross(Z,Y);

	float3x3 xform = { X, Y, Z };
	
	// Output 4x4 control points
	ControlPoint p;
	p.position = arrControl_Points[ctrlPoint_index];
	p.position = mul(p.position, xform) + vPosition;
	return p;
}
//_______________


[domain("quad")]
DS_OUT	DS_BezSurface(quadTessFactors tessFactors,	float2 coords : SV_DomainLocation, 
												const OutputPatch<ControlPoint, 16> patch)
{
	DS_OUT dout;
	
	float4 basisU =  Bernstein(coords.x);
	float4 basisV =  Bernstein(coords.y);

	float4 basisdU = dBernstein(coords.x);
	float4 basisdV = dBernstein(coords.y);


	float3 pos = evaluateBezierSurface(patch, basisU, basisV);
	
	
	float3 tangent =   evaluateBezierSurface(patch, basisdU, basisV);
	float3 bitangent = evaluateBezierSurface(patch, basisU,  basisdV);
	float3 normal = normalize(cross(tangent, bitangent));


	float h = getTerrainHeight(vPosition+pos);
	pos.y += h - 10.0f;

	// position
	dout.position = mul(float4(pos,1.0f), m_WorldViewProj);
	// normal
	dout.wNormal = normal; 

	return dout;
}

//_______________


float4 PS_BezSurface(DS_OUT pin) : SV_TARGET
{

	return float4(shade(pin.wNormal), 1.0f);
}


//###################################################################################


DS_OUT VS_NoTess(VS_IN vin)
{
	DS_OUT dout = (DS_OUT)0;
	dout.position = mul(float4(vin.position, 1.0f), m_WorldViewProj);
	return dout;

}

float4 PS_NoTess(DS_OUT pin) : SV_TARGET
{
	return float4(1,0,0,1);
}

//###################################################################################



RasterizerState rs_None
{
	CullMode = None;
	
};

technique11 TessBezierSurface_Integer
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_5_0,  VS_BezSurface()   ) );
		SetHullShader ( CompileShader ( hs_5_0, ControlPointHS_BezSurface_Integer() ) );
		SetDomainShader( CompileShader ( ds_5_0, DS_BezSurface() ) );
		SetPixelShader( CompileShader( ps_5_0,   PS_BezSurface()   ) );

		SetRasterizerState(rs_None);
	}
}

technique11 TessBezierSurface_FracEven
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_5_0,  VS_BezSurface()   ) );
		SetHullShader ( CompileShader ( hs_5_0, ControlPointHS_BezSurface_FracEven() ) );
		SetDomainShader( CompileShader ( ds_5_0, DS_BezSurface() ) );
		SetPixelShader( CompileShader( ps_5_0,   PS_BezSurface()   ) );

		SetRasterizerState(rs_None);
	}
}

technique11 TessBezierSurface_FracOdd
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_5_0,  VS_BezSurface()   ) );
		SetHullShader ( CompileShader ( hs_5_0, ControlPointHS_BezSurface_FracOdd() ) );
		SetDomainShader( CompileShader ( ds_5_0, DS_BezSurface() ) );
		SetPixelShader( CompileShader( ps_5_0,   PS_BezSurface()   ) );

		SetRasterizerState(rs_None);
	}
}


technique11 NoTess
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_5_0,  VS_NoTess()   ) );
		SetPixelShader( CompileShader( ps_5_0,   PS_NoTess()   ) );
	}


}